﻿//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Microsoft Public License.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace DSPTest
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data.Services;
    using System.Data.Services.Providers;
    using DataServiceProvider;

    public class Quantity
    {
        public double Value { get; set; }
        public string Units { get; set; }
    }

    public class ClientProduct
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public DateTime ReleaseDate { get; set; }
        public DateTime? DiscontinueDate { get; set; }
        public int Rating { get; set; }
        public Quantity Quantity { get; set; }
        public ClientCategory Category { get; set; }
    }

    public class ClientCategory
    {
        public ClientCategory()
        {
            this.Products = new List<ClientProduct>();
        }

        public int ID { get; set; }
        public string Name { get; set; }
        public List<ClientProduct> Products { get; set; }
    }

    public class ProductsService : DSPDataService<DSPContext>
    {
        private static DSPContext context;
        private static DSPMetadata metadata;

        static ProductsService()
        {
            metadata = new DSPMetadata("ProductsContext", "DSPTest");

            var quantityType = metadata.AddComplexType("Quantity");
            metadata.AddPrimitiveProperty(quantityType, "Value", typeof(double));
            metadata.AddPrimitiveProperty(quantityType, "Units", typeof(string));

            var productType = metadata.AddEntityType("Product");
            metadata.AddKeyProperty(productType, "ID", typeof(int));
            metadata.AddPrimitiveProperty(productType, "Name", typeof(string));
            metadata.AddPrimitiveProperty(productType, "Description", typeof(string));
            metadata.AddPrimitiveProperty(productType, "ReleaseDate", typeof(DateTime));
            metadata.AddPrimitiveProperty(productType, "DiscontinueDate", typeof(DateTime?));
            metadata.AddPrimitiveProperty(productType, "Rating", typeof(int));
            metadata.AddComplexProperty(productType, "Quantity", quantityType);

            var productsSet = metadata.AddResourceSet("Products", productType);

            var categoryType = metadata.AddEntityType("Category");
            metadata.AddKeyProperty(categoryType, "ID", typeof(int));
            metadata.AddPrimitiveProperty(categoryType, "Name", typeof(string));

            var categoriesSet = metadata.AddResourceSet("Categories", categoryType);

            metadata.AddResourceReferenceProperty(productType, "Category", categoriesSet);
            metadata.AddResourceSetReferenceProperty(categoryType, "Products", productsSet);

            context = CreateDataContext();
        }

        public static void ResetDataContext()
        {
            context = CreateDataContext();
        }

        public static IDisposable RestoreDataContext()
        {
            return new RestoreDataContextDisposable();
        }

        private class RestoreDataContextDisposable : IDisposable
        {
            #region IDisposable Members

            public void Dispose()
            {
                ProductsService.ResetDataContext();
            }

            #endregion
        }

        private static DSPContext CreateDataContext()
        {
            context = new DSPContext();
            ResourceType quantityType, productType, categoryType;
            metadata.TryResolveResourceType("DSPTest.Quantity", out quantityType);
            metadata.TryResolveResourceType("DSPTest.Product", out productType);
            metadata.TryResolveResourceType("DSPTest.Category", out categoryType);
            IList<DSPResource> products = context.GetResourceSetEntities("Products");
            IList<DSPResource> categories = context.GetResourceSetEntities("Categories");

            var categoryFood = new DSPResource(categoryType);
            categoryFood.SetValue("ID", 0);
            categoryFood.SetValue("Name", "Food");
            categoryFood.SetValue("Products", new List<DSPResource>());
            categories.Add(categoryFood);

            var categoryBeverages = new DSPResource(categoryType);
            categoryBeverages.SetValue("ID", 1);
            categoryBeverages.SetValue("Name", "Beverages");
            categoryBeverages.SetValue("Products", new List<DSPResource>());
            categories.Add(categoryBeverages);

            var categoryElectronics = new DSPResource(categoryType);
            categoryElectronics.SetValue("ID", 2);
            categoryElectronics.SetValue("Name", "Electronics");
            categoryElectronics.SetValue("Products", new List<DSPResource>());
            categories.Add(categoryElectronics);

            var productBread = new DSPResource(productType);
            productBread.SetValue("ID", 0);
            productBread.SetValue("Name", "Bread");
            productBread.SetValue("Description", "Whole grain bread");
            productBread.SetValue("ReleaseDate", new DateTime(1992, 1, 1));
            productBread.SetValue("DiscontinueDate", null);
            productBread.SetValue("Rating", 4);
            var quantity = new DSPResource(quantityType);
            quantity.SetValue("Value", (double)12);
            quantity.SetValue("Units", "pieces");
            productBread.SetValue("Quantity", quantity);
            productBread.SetValue("Category", categoryFood);
            products.Add(productBread);

            var productMilk = new DSPResource(productType);
            productMilk.SetValue("ID", 1);
            productMilk.SetValue("Name", "Milk");
            productMilk.SetValue("Description", "Low fat milk");
            productMilk.SetValue("ReleaseDate", new DateTime(1995, 10, 21));
            productMilk.SetValue("DiscontinueDate", null);
            productMilk.SetValue("Rating", 3);
            quantity = new DSPResource(quantityType);
            quantity.SetValue("Value", (double)4);
            quantity.SetValue("Units", "liters");
            productMilk.SetValue("Quantity", quantity);
            productMilk.SetValue("Category", categoryBeverages);
            products.Add(productMilk);

            var productWine = new DSPResource(productType);
            productWine.SetValue("ID", 2);
            productWine.SetValue("Name", "Wine");
            productWine.SetValue("Description", "Red wine, year 2003");
            productWine.SetValue("ReleaseDate", new DateTime(2003, 11, 24));
            productWine.SetValue("DiscontinueDate", new DateTime(2008, 3, 1));
            productWine.SetValue("Rating", 5);
            quantity = new DSPResource(quantityType);
            quantity.SetValue("Value", (double)7);
            quantity.SetValue("Units", "bottles");
            productWine.SetValue("Quantity", quantity);
            productWine.SetValue("Category", categoryBeverages);
            products.Add(productWine);

            ((List<DSPResource>)categoryFood.GetValue("Products")).Add(productBread);
            ((List<DSPResource>)categoryBeverages.GetValue("Products")).Add(productMilk);
            ((List<DSPResource>)categoryBeverages.GetValue("Products")).Add(productWine);

            return context;
        }

        protected override DSPContext CreateDataSource()
        {
            return context;
        }

        protected override DSPMetadata CreateDSPMetadata()
        {
            return metadata;
        }

        public static void InitializeService(DataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("*", EntitySetRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = System.Data.Services.Common.DataServiceProtocolVersion.V2;
            config.DataServiceBehavior.AcceptCountRequests = true;
            config.DataServiceBehavior.AcceptProjectionRequests = true;
            config.UseVerboseErrors = true;
        }
    }
}
